/*
 * Copyright (c) 2013-2018 上海汇数数据. All rights reserved.
 * @(#) ToStrings.java 2018-08-05 20:23
 */

package cn.jh.common.core.base;

import com.google.common.base.Ascii;
import com.google.common.base.CaseFormat;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

import static com.google.common.base.MoreObjects.firstNonNull;


/**
 * @author Fuchun
 * @since 1.0
 */
public abstract class ToStrings {

    private static final String DEFAULT_DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    private static final org.joda.time.format.DateTimeFormatter DATE_TIME_FORMATTER =
            org.joda.time.format.DateTimeFormat.forPattern(DEFAULT_DATE_TIME_PATTERN);
    private static final DateTimeFormatter JAVA_DATE_TIME_FORMATTER =
            DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_PATTERN);
    private static final ThreadLocal<DateFormat> THREAD_DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat(DEFAULT_DATE_TIME_PATTERN));

    private ToStrings() {
    }

    /**
     * 省略较长的内容字符串，如果长度大于指定的最大值，则末尾以 "..." 结束。
     * <pre>
     * String text = "我们都是神枪手，每一颗子弹消灭一个敌人";
     * ellipsis(text, 12)   =&gt;  "我们都是神枪手，每一..."
     * </pre>
     *
     * @param input     内容字符串。
     * @param maxLength 最大长度。
     * @return 返回省略后的内容字符串。
     * @since 1.0
     */
    @Nullable
    @Contract("null, _ -> param1")
    public static String ellipsis(String input, int maxLength) {
        if (input == null || input.isEmpty()) {
            return input;
        }
        return Ascii.truncate(input, maxLength, "...");
    }

    /**
     * 修剪指定的字符串。
     *
     * @param input     内容字符串。
     * @param maxLength 显示的最大长度。
     * @return 修剪后的字符串。
     * @since 1.0
     */
    @Nullable
    @Contract("null, _ -> param1")
    public static String clip(String input, int maxLength) {
        if (input == null || input.isEmpty()) {
            return input;
        }
        if (maxLength <= 0 || input.length() <= maxLength) {
            return input;
        } else {
            return input.substring(0, maxLength);
        }
    }

    @Nullable
    @Contract("null -> null; !null -> !null")
    public static String formatDate(java.util.Date date) {
        if (date == null) return null;
        return THREAD_DATE_FORMAT.get().format(date);
    }

    @Nullable
    @Contract("null -> null")
    public static String formatDate(org.joda.time.DateTime dateTime) {
        if (dateTime == null) {
            return null;
        }
        return dateTime.toString(DATE_TIME_FORMATTER);
    }

    @Nullable
    public static String formatUTC(long epochMillis) {
        return formatTimestamp(epochMillis, ZoneOffset.UTC);
    }

    @Nullable
    public static String formatTimestamp(long epochMillis, ZoneOffset offset) {
        if (epochMillis <= 0) {
            return null;
        }
        ZoneOffset zoneOffset = offset == null ? ZoneOffset.UTC : offset;
        return Instant.ofEpochMilli(epochMillis).atOffset(zoneOffset).format(JAVA_DATE_TIME_FORMATTER);
    }

    @NotNull
    public static String conceal(String input) {
        if (input == null || input.isEmpty()) {
            return "";
        }
        int length = input.length();
        if (length == 1) {
            return "***";
        } else if (length <= 5) {
            return input.substring(0, 1) + "***";
        } else if (length <= 10) {
            return String.join("", input.substring(0, 2),
                    "***", input.substring(length - 2, length));
        } else {
            return String.join("", input.substring(0, 3),
                    "******", input.substring(length - 3, length));
        }
    }

    /**
     * 字符串格式转换：data 属性名称转换为驼峰式名称。
     * <pre>{@code
     * foo        =&gt;      foo
     * foo-bar    =&gt;      fooBar
     * foo-bar-zo =&gt;      fooBarZo
     * }</pre>
     *
     * @param dataAttr data 属性名称
     * @return 返回驼峰式属性名称。
     * @since 1.0
     */
    public static String dataAttr2Camel(String dataAttr) {
//        return dataAttr2Camel(dataAttr, false);
        return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, dataAttr);
    }

    /**
     * 字符串格式转换：data 属性名称转换为驼峰式名称。
     * <pre>{@code
     * dataAttr2Camel("foo", false)        =&gt;      foo
     * dataAttr2Camel("foo", true)         =&gt;      Foo
     * dataAttr2Camel("foo-bar", false)    =&gt;      fooBar
     * dataAttr2Camel("foo-bar", true )    =&gt;      FooBar
     * dataAttr2Camel("foo-bar-zo", false) =&gt;      fooBarZo
     * dataAttr2Camel("foo-bar-zo", true)  =&gt;      FooBarZo
     * }</pre>
     *
     * @param dataAttr html5 data 属性名称。
     * @param isInitial 首字符是否大写。
     * @return 转换后的字符串。
     * @since 1.0
     */
    @Nullable
    @Contract("null, _ -> null")
    public static String dataAttr2Camel(String dataAttr, boolean isInitial) {
        if (dataAttr == null || dataAttr.isEmpty()) {
            return dataAttr;
        }
        CaseFormat target = isInitial ? CaseFormat.UPPER_CAMEL : CaseFormat.LOWER_CAMEL;
        return CaseFormat.LOWER_HYPHEN.to(target, dataAttr);
    }

    /**
     * 字符串格式转换：驼峰式名称转换为 html5 data attribute 名称。
     * <p />
     * {@link #camel2DataAttr(String, String, String) camel2DataAttr(camel, null, null)} 的快捷调用。
     *
     * @param camel 驼峰式名称。
     * @return 返回 data 式属性名称。
     * @since 1.0
     */
    public static String camel2DataAttr(String camel) {
        return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, camel);
    }

    /**
     * 字符串格式转换：驼峰式名称转换为 html5 data attribute 名称。
     * <pre>{@code
     * foo          =&gt;     foo
     * fooBar       =&gt;     foo-bar
     * fooBarZo     =&gt;     foo-bar-zo
     * }</pre>
     *
     * @param camel 驼峰式名称。
     * @param prefix 生成的字符串的前缀（maybe null）。
     * @param suffix 生成的字符串后缀（maybe null）。
     * @return 返回 data 式属性名称。
     * @since 1.0
     */
    @NotNull
    public static String camel2DataAttr(String camel, String prefix, String suffix) {
        prefix = firstNonNull(prefix, "");
        suffix = firstNonNull(suffix, "");
        return String.join("", prefix, CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, camel), suffix);
    }

    /**
     * 以单词间下划线连接的方式转换指定的驼峰式字符串。
     * <p/>
     * {@link #camel2Underscore(String, String, String) camel2Underscore(camel, null, null)} 的快捷调用。
     *
     * @param camel  驼峰式命名的字符串
     * @return 下划线连接单词的字符串。
     * @since 1.0
     */
    @Contract("null -> null")
    public static String camel2Underscore(String camel) {
        return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, camel);
    }

    /**
     * 以单词间下划线连接的方式转换指定的驼峰式字符串。
     *
     * @param camel  驼峰式命名的字符串
     * @param prefix 生成的字符串的前缀（maybe null）。
     * @param suffix 生成的字符串后缀（maybe null）。
     * @return 下划线连接单词的字符串。
     * @since 1.0
     */
    @NotNull
    public static String camel2Underscore(String camel, String prefix, String suffix) {
        prefix = firstNonNull(prefix, "");
        suffix = firstNonNull(suffix, "");
        return String.join("", prefix, CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, camel), suffix);
    }
}
